
#include "cvf_PixelSampler.h"

// [*] CPointInRectangleRandomSampler

void CPointInRectangleRandomSampler::Work(int W, int H, int SampleMargin, double SampleRate)
{
	CSimpleTypeArray<int> tmp_list;
	tmp_list.Clear();
	Work(W, H, SampleMargin, SampleRate, tmp_list);
}

void CPointInRectangleRandomSampler::Work(int W, int H, int SampleMargin, double SampleRate, const CSimpleTypeArray<int>& PreselectedPointList)
{
	Work(W, H, SampleMargin, SampleRate, SampleRate, PreselectedPointList);
}

void CPointInRectangleRandomSampler::Work(int W, int H, int SampleMargin, double SampleRate_Pre, double SampleRate_Rnd, const CSimpleTypeArray<int>& PreselectedPointList)
{
	this->W = W; this->H = H; WH = W * H;
	NearestSampledPixelList.Clear();

	double sr;
	int i,j,t,ix,iy,jjx,jjy,jjx2,jx,jy;
	int qt, qh;
	int* q2map = new int[WH];
	int* map2q = new int[WH];
	bool* force = new bool[WH];

	SampledPixelList.Clear();
	for ( i = 0; i < WH; ++i )
	{
		force[i] = false;
		map2q[i] = -1;
	}
	qh = qt = 0;
	for ( i = 0; i < (int)PreselectedPointList.Size; ++i )
	{
		t = PreselectedPointList.pElement[i];
		if (t<0 || t>=WH) continue;
		//if (CMyRandom::NextDouble() < SampleRate) SampledPixelList.PushBack(t);
		force[t] = true;
		q2map[qt] = t;
		map2q[t] = qt;
		++qh; ++qt;
	}
	for ( i = 0; i < WH; ++i )
	{
		if (map2q[i] >= 0) continue;
		q2map[qt] = i;
		map2q[i] = qt;
		++qt;
	}

#if 1

	int da, db, ea, eb;
	int xa, xb, ya, yb;
	double rn, rm;
	rn = rm = 0.0;

	xa = xb = ya = yb = 0;
	ea = qh; eb = qt - qh;
	da = CMyMath::TranslateNumber_Round(ea * SampleRate_Pre, (int)0); da = (da<0 ? 0 : (da>ea ? ea : da));
	db = CMyMath::TranslateNumber_Round(eb * SampleRate_Rnd, (int)0); db = (db<0 ? 0 : (db>eb ? eb : db));
	// x: target passed
	// y: total passed
	// d: target left
	// e: total left

	for ( i = 0; i < qt; ++i )
	{
		t = q2map[i]; ix = t % W; iy = t / W;
		if (map2q[t] < 0) continue;
		sr = (i<qh ? SampleRate_Pre : SampleRate_Rnd);

		if (i < qh)
		{
			rn = abs((xa+1+EPS)/(ya+1+EPS) - (da-1+EPS)/(ea-1+EPS));
			rm = abs((xa+0+EPS)/(ya+1+EPS) - (da-0+EPS)/(ea-1+EPS));
			if (rn < rm)
			{
				++xa; --da;
				SampledPixelList.PushBack(t);
			}
			++ya; --ea;
		}
		else
		{
			rn = abs((xb+1+EPS)/(yb+1+EPS) - (db-1+EPS)/(eb-1+EPS));
			rm = abs((xb+0+EPS)/(yb+1+EPS) - (db-0+EPS)/(eb-1+EPS));
			if (rn < rm)
			{
				++xb; --db;
				SampledPixelList.PushBack(t);
			}
			++yb; --eb;
		}
		//rn = CMyRandom::NextDouble();
		//if (rn < sr) SampledPixelList.PushBack(t);

		for ( jjy = -SampleMargin; jjy <= +SampleMargin; ++jjy)
		{
			jjx2 = SampleMargin; // - abs(jjy);
			for ( jjx = -jjx2; jjx <= +jjx2; ++jjx)
			{
				jx = ix + jjx; if (jx<0 || jx>=W) continue;
				jy = iy + jjy; if (jy<0 || jy>=H) continue;

				t = jx + jy * W;
				j = map2q[t]; if (j < 0) continue;

				if (!force[t])
					map2q[t] = -1;
			}
		}
	}
	//cout<<endl;
#else
	int o;
	while (qt > 0)
	{
		i = --qh; sr = SampleRate_Pre;
		if (i < 0)
		{
			sr = SampleRate_Rnd;

			i = qt / 2;
			//i = CMyRandom::NextInt(qt);
		}

		t = q2map[i]; ix = t % W; iy = t / W;
		if (CMyRandom::NextDouble() < sr) SampledPixelList.PushBack(t);

		for ( jjy = -SampleMargin; jjy <= +SampleMargin; ++jjy)
		{
			jjx2 = SampleMargin - abs(jjy);
			for ( jjx = -jjx2; jjx <= +jjx2; ++jjx)
			{
				jx = ix + jjx; if (jx<0 || jx>=W) continue;
				jy = iy + jjy; if (jy<0 || jy>=H) continue;

				t = jx + jy * W;
				j = map2q[t]; if (j < 0) continue;
				if (i!=j && force[t]) continue;

				o = q2map[--qt];
				q2map[j] = o; map2q[o] = j;
				//q2map[qt] = t; map2q[t] = qt;
				map2q[t] = -1;
			}
		}
	}
#endif

	delete[] force;
	delete[] q2map;
	delete[] map2q;
}

void CPointInRectangleRandomSampler::ComputeNearestSampledPointList()
{
	NearestSampledPixelList.Resize(WH);
	NearestSampledPixelList.Fill(-1);

	int* queue = new int[WH];
	int* visit = NearestSampledPixelList.pElement;
	int qh, qt;
	int i, ix, iy, it;
	int j, jx, jy, jt;

	for ( qt = i = 0; i < (int)SampledPixelList.Size; ++i )
	{
		qh = SampledPixelList.pElement[i];
		queue[qt++] = qh;
		visit[qh] = qh;
	}
	for ( qh = 0; qh < qt; ++qh )
	{
		it = queue[qh]; ix = it % W; iy = it / W;
		for ( j = 0; j < 4; ++j )
		{
			jx = ix + (j==0 ? -1 : (j==2 ? +1 : 0));
			jy = iy + (j==1 ? -1 : (j==3 ? +1 : 0));
			jt = jx + jy * W;
			if (jx<0 || jx>=W || jy<0 || jy>=H || visit[jt]>=0) continue;
			queue[qt++] = jt;
			visit[jt] = visit[it];
		}
	}

	delete[] queue;
}

// [*] CImagePixelSampler_EdgeAndRandom

void CImagePixelSampler_EdgeAndRandom::myRelease()
{
	CDataStructureTools::MyRelease_Single(_pIFB);
}

CImagePixelSampler_EdgeAndRandom::CImagePixelSampler_EdgeAndRandom()
{
	_pIFB = NULL;
	SetDefaultParameters();
}

CImagePixelSampler_EdgeAndRandom::~CImagePixelSampler_EdgeAndRandom()
{
	myRelease();
}

void CImagePixelSampler_EdgeAndRandom::SetDefaultParameters()
{
	ColorMode = COLOR_MODE_GRAY;
	Canny_GaussianSigma = 1.0;
	Canny_tLow = 0.30;
	Canny_tHigh = 0.60;
	Edge_LengthThreshold = 10;
	Edge_DirectionThreshold = PI / 6.0;
	Edge_SampleRate = 1.0;
	Random_SampleMargin = 5;
	Random_SampleRate = 1.0;
	BoundaryMargin = 0;
}

void CImagePixelSampler_EdgeAndRandom::Prepare()
{
	myRelease();

	_pIFB = new CImageFilterBank();
	CImageFilter* pfRGB = _pIFB->pBasicFilter_RGB;

	CImageFilter* pfCanny = new CImageFilter_CannyEdge(Canny_GaussianSigma, Canny_tLow, Canny_tHigh, 0.0, PI+PI, true);
	if (ColorMode == COLOR_MODE_GRAY)
	{
		CImageFilter* pfGray = new CImageFilter_Gray();
		pfGray->PushBackInputResponse(pfRGB, 0);
		pfGray->PushBackInputResponse(pfRGB, 1);
		pfGray->PushBackInputResponse(pfRGB, 2);
		_pIFB->PushBackFilter(pfGray);

		pfCanny->PushBackInputResponse(pfGray, 0);
	}
	else if (ColorMode == COLOR_MODE_RGB)
	{
		pfCanny->PushBackInputResponse(pfRGB, 0);
		pfCanny->PushBackInputResponse(pfRGB, 1);
		pfCanny->PushBackInputResponse(pfRGB, 2);
	}
	else
	{
		if (ColorMode < 0) ColorMode = 0;
		if (ColorMode > 2) ColorMode = 2;
		pfCanny->PushBackInputResponse(pfRGB, ColorMode);
	}
	_pIFB->PushBackFilter(pfCanny);

	CImageFilter_EdgeForest* pfEdgeForest = new CImageFilter_EdgeForest(0.5, true, Edge_LengthThreshold, Edge_DirectionThreshold);
	pfEdgeForest->PushBackInputResponse(pfCanny, 0);
	pfEdgeForest->PushBackInputResponse(pfCanny, 1);
	_pIFB->PushBackFilter(pfEdgeForest);

	pfEdgeForest->PushBackOutput(0);
	_pIFB->GetOutputDimension(true);
}

void CImagePixelSampler_EdgeAndRandom::Work(CSimpleTypeArray<int>& DstSampledPixelList, const CImg<int>& imgSrc)
{
	DstSampledPixelList.Clear();

	if (_pIFB == NULL) Prepare();

	_pIFB->Work(imgSrc);
	const double* qe = _pIFB->GetOutput(0);

	int W = imgSrc.width;
	int H = imgSrc.height;
	int WH = W * H;
	int i, j, tx, ty;

	//CImg<double> imgDouble;
	//CImg<int> imgOutput;
	//imgDouble.assign(qe, W, H, 1, 1, true);
	//CExternalTools::NormalizeToGeneralIntGrayscaleImage(imgDouble, imgOutput);
	////imgOutput.display();
	//CExternalTools::mySaveImage("a.jpg", imgOutput);

	for ( i = 0; i < WH; ++i )
		if (Edge_LengthThreshold>0 && qe[i]>0.5)
			DstSampledPixelList.PushBack(i);

	_pIFB->OutputList.DeepClear();

	CPointInRectangleRandomSampler ips;
	ips.Work(W, H, Random_SampleMargin, Edge_SampleRate, Random_SampleRate, DstSampledPixelList);

	DstSampledPixelList.Clear();
	const int* qip = ips.SampledPixelList.pElement;
	j = (int)ips.SampledPixelList.Size;
	for ( i = 0; i < j; ++i, ++qip )
	{
		tx = *qip % W;
		ty = *qip / W;
		if (tx<BoundaryMargin || tx>=W-BoundaryMargin) continue;
		if (ty<BoundaryMargin || ty>=H-BoundaryMargin) continue;
		DstSampledPixelList.PushBack(*qip);
	}
}

